/**
 * ***************************************************************************** Copyright (c) 2003,
 * 2006 Subclipse project and others. All rights reserved. This program and the accompanying
 * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * <p>Contributors: Subclipse project committers - initial API and implementation
 * ****************************************************************************
 */
package org.tigris.subversion.subclipse.ui.repository;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.SameShellProvider;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.PluginTransfer;
import org.eclipse.ui.part.PluginTransferData;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.part.WorkbenchPart;
import org.tigris.subversion.subclipse.core.ISVNRemoteFile;
import org.tigris.subversion.subclipse.core.ISVNRemoteFolder;
import org.tigris.subversion.subclipse.core.ISVNRemoteResource;
import org.tigris.subversion.subclipse.core.ISVNRepositoryLocation;
import org.tigris.subversion.subclipse.core.ISVNResource;
import org.tigris.subversion.subclipse.core.SVNProviderPlugin;
import org.tigris.subversion.subclipse.core.resources.RemoteFile;
import org.tigris.subversion.subclipse.ui.IHelpContextIds;
import org.tigris.subversion.subclipse.ui.ISVNUIConstants;
import org.tigris.subversion.subclipse.ui.Policy;
import org.tigris.subversion.subclipse.ui.SVNUIPlugin;
import org.tigris.subversion.subclipse.ui.WorkspacePathValidator;
import org.tigris.subversion.subclipse.ui.actions.OpenRemoteFileAction;
import org.tigris.subversion.subclipse.ui.actions.RemoteResourceTransfer;
import org.tigris.subversion.subclipse.ui.actions.SVNAction;
import org.tigris.subversion.subclipse.ui.editor.RemoteFileEditorInput;
import org.tigris.subversion.subclipse.ui.repository.model.AllRootsElement;
import org.tigris.subversion.subclipse.ui.repository.model.RemoteContentProvider;
import org.tigris.subversion.subclipse.ui.wizards.ClosableWizardDialog;
import org.tigris.subversion.subclipse.ui.wizards.NewLocationWizard;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizard;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardDialog;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardNewRepositoryPage;

/**
 * RepositoriesView is a view on a set of known SVN repositories which allows navigation of the
 * structure of the repository and the performing of SVN-specific operations on the repository
 * contents.
 */
public class RepositoriesView extends ViewPart implements ISelectionListener {
  public static final String VIEW_ID =
      "org.tigris.subversion.subclipse.ui.repository.RepositoriesView"; //$NON-NLS-1$

  // The root
  private AllRootsElement root;

  // Actions
  private Action newAction;
  private RemoveRootAction removeRootAction;

  // The tree viewer
  protected TreeViewer treeViewer;

  // Drill down adapter
  private DrillDownAdapter drillPart; // Home, back, and "drill into"

  private Action refreshAction;
  private Action refreshPopupAction;
  private Action collapseAllAction;
  private OpenRemoteFileAction openAction;
  private Action propertiesAction;

  private RemoteContentProvider contentProvider;
  // this listener is used when a repository is added, removed or changed
  private IRepositoryListener repositoryListener =
      new IRepositoryListener() {
        public void repositoryAdded(final ISVNRepositoryLocation root) {
          getViewer()
              .getControl()
              .getDisplay()
              .syncExec(
                  new Runnable() {
                    public void run() {
                      refreshViewer(null, false);
                      getViewer().setSelection(new StructuredSelection(root));
                    }
                  });
        }

        public void repositoryRemoved(ISVNRepositoryLocation root) {
          refresh(null, false);
        }

        public void repositoriesChanged(ISVNRepositoryLocation[] roots) {
          refresh(null, false);
        }

        public void remoteResourceDeleted(ISVNRemoteResource resource) {
          refresh(resource.getParent(), false);
        }

        public void remoteResourceCreated(ISVNRemoteFolder parent, String resourceName) {
          refresh(parent, true);
        }

        public void remoteResourceCopied(ISVNRemoteResource source, ISVNRemoteFolder destination) {
          refresh(destination, false);
        }

        public void remoteResourceMoved(
            ISVNRemoteResource resource,
            ISVNRemoteFolder destinationFolder,
            String destinationResourceName) {
          refresh(resource.getParent(), false);
          refresh(destinationFolder, false);
        }

        private void refresh(Object object, boolean refreshRepositoriesFolders) {
          final Object finalObject = object;
          final boolean finalRefreshReposFolders = refreshRepositoriesFolders;
          Display display = getViewer().getControl().getDisplay();
          display.syncExec(
              new Runnable() {
                public void run() {
                  RepositoriesView.this.refreshViewer(finalObject, finalRefreshReposFolders);
                }
              });
        }

        public void repositoryModified(ISVNRepositoryLocation root) {
          refresh(null, false);
        }
      };

  private static final class RepositoryDragSourceListener implements DragSourceListener {
    private IStructuredSelection selection;

    public void dragStart(DragSourceEvent event) {
      if (selection != null) {
        final Object[] array = selection.toArray();
        // event.doit = Utils.getResources(array).length > 0;
        for (int i = 0; i < array.length; i++) {
          if (array[i] instanceof ISVNRemoteResource) {
            event.doit = true;
            return;
          }
        }
        event.doit = false;
      }
    }

    public void dragSetData(DragSourceEvent event) {
      if (selection != null
          && RemoteResourceTransfer.getInstance().isSupportedType(event.dataType)) {
        final Object[] array = selection.toArray();
        for (int i = 0; i < array.length; i++) {
          if (array[i] instanceof ISVNRemoteResource) {
            event.data = array[i];
            return;
          }
        }
      } else if (PluginTransfer.getInstance().isSupportedType(event.dataType)) {
        final Object[] array = selection.toArray();
        for (int i = 0; i < array.length; i++) {
          if (array[i] instanceof ISVNRemoteResource) {
            event.data =
                new PluginTransferData(
                    "org.tigris.subversion.subclipse.ui.svnRemoteDrop",
                    RemoteResourceTransfer.getInstance()
                        .toByteArray((ISVNRemoteResource) array[i])); // $NON-NLS-1$
            return;
          }
        }
      }
    }

    public void dragFinished(DragSourceEvent event) {}

    public void updateSelection(IStructuredSelection selection) {
      this.selection = selection;
    }
  }

  RepositoryDragSourceListener repositoryDragSourceListener;

  /**
   * Constructor for RepositoriesView.
   *
   * @param partName
   */
  public RepositoriesView() {
    //	super(VIEW_ID);
  }

  /** Contribute actions to the view */
  protected void contributeActions() {

    final Shell shell = getShell();

    // Create actions

    // New Repository (popup)
    newAction =
        new Action(
            Policy.bind("RepositoriesView.new"), // $NON-NLS-1$
            SVNUIPlugin.getPlugin()
                .getImageDescriptor(ISVNUIConstants.IMG_NEWLOCATION)) { // $NON-NLS-1$
          public void run() {
            if (!WorkspacePathValidator.validateWorkspacePath()) return;
            NewLocationWizard wizard = new NewLocationWizard();
            WizardDialog dialog = new ClosableWizardDialog(shell, wizard);
            dialog.open();
          }
        };
    PlatformUI.getWorkbench()
        .getHelpSystem()
        .setHelp(newAction, IHelpContextIds.NEW_REPOSITORY_LOCATION_ACTION);

    // Properties
    propertiesAction = new PropertyDialogAction(new SameShellProvider(shell), getViewer());
    getViewSite()
        .getActionBars()
        .setGlobalActionHandler(ActionFactory.PROPERTIES.getId(), propertiesAction);
    IStructuredSelection selection = (IStructuredSelection) getViewer().getSelection();
    if (selection.size() == 1 && selection.getFirstElement() instanceof ISVNRepositoryLocation) {
      propertiesAction.setEnabled(true);
    } else {
      propertiesAction.setEnabled(false);
    }
    getViewer()
        .addSelectionChangedListener(
            new ISelectionChangedListener() {
              public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection ss = (IStructuredSelection) event.getSelection();
                boolean enabled =
                    ss.size() == 1 && ss.getFirstElement() instanceof ISVNRepositoryLocation;
                propertiesAction.setEnabled(enabled);
              }
            });

    // Remove Root
    removeRootAction = new RemoveRootAction(treeViewer.getControl().getShell());
    removeRootAction.selectionChanged((IStructuredSelection) null);
    PlatformUI.getWorkbench()
        .getHelpSystem()
        .setHelp(removeRootAction, IHelpContextIds.REMOVE_REPOSITORY_LOCATION_ACTION);

    IActionBars bars = getViewSite().getActionBars();
    bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), removeRootAction);

    // Refresh action (toolbar)
    SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
    refreshAction =
        new Action(
            Policy.bind("RepositoriesView.refresh"),
            SVNUIPlugin.getPlugin()
                .getImageDescriptor(ISVNUIConstants.IMG_REFRESH_ENABLED)) { // $NON-NLS-1$
          public void run() {
            refreshViewer(null, true);
          }
        };
    refreshAction.setToolTipText(Policy.bind("RepositoriesView.refreshTooltip")); // $NON-NLS-1$
    refreshAction.setDisabledImageDescriptor(
        plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH_DISABLED));
    refreshAction.setHoverImageDescriptor(plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH));
    getViewSite()
        .getActionBars()
        .setGlobalActionHandler(ActionFactory.REFRESH.getId(), refreshAction);

    refreshPopupAction =
        new Action(
            Policy.bind("RepositoriesView.refreshPopup"),
            SVNUIPlugin.getPlugin()
                .getImageDescriptor(ISVNUIConstants.IMG_REFRESH)) { // $NON-NLS-1$
          public void run() {
            refreshViewerNode();
          }
        };

    // Collapse action
    collapseAllAction =
        new Action(
            Policy.bind("RepositoriesView.collapseAll"),
            SVNUIPlugin.getPlugin()
                .getImageDescriptor(ISVNUIConstants.IMG_COLLAPSE_ALL_ENABLED)) { // $NON-NLS-1$
          public void run() {
            collapseAll();
          }
        };
    collapseAllAction.setToolTipText(
        Policy.bind("RepositoriesView.collapseAllTooltip")); // $NON-NLS-1$
    collapseAllAction.setHoverImageDescriptor(
        plugin.getImageDescriptor(ISVNUIConstants.IMG_COLLAPSE_ALL));

    // Create the popup menu
    MenuManager menuMgr = new MenuManager();
    Tree tree = treeViewer.getTree();
    Menu menu = menuMgr.createContextMenu(tree);
    menuMgr.addMenuListener(
        new IMenuListener() {
          public void menuAboutToShow(IMenuManager manager) {
            addWorkbenchActions(manager);
          }
        });
    menuMgr.setRemoveAllWhenShown(true);
    tree.setMenu(menu);
    getSite().registerContextMenu(menuMgr, treeViewer);

    // Create the local tool bar
    IToolBarManager tbm = bars.getToolBarManager();
    drillPart.addNavigationActions(tbm);
    tbm.add(refreshAction);
    tbm.add(new Separator());
    tbm.add(collapseAllAction);
    tbm.update(false);

    // Create the open action for double clicks
    openAction = new OpenRemoteFileAction();
    bars.updateActionBars();

    IActionBars actionBars = getViewSite().getActionBars();
    IMenuManager actionBarsMenu = actionBars.getMenuManager();
    Action newRepositoryAction =
        new Action(Policy.bind("RepositoriesView.newRepository")) { // $NON-NLS-1$
          public void run() {
            SvnWizardNewRepositoryPage newRepositoryPage = new SvnWizardNewRepositoryPage();
            SvnWizard wizard = new SvnWizard(newRepositoryPage);
            SvnWizardDialog dialog = new SvnWizardDialog(getShell(), wizard);
            if (dialog.open() == SvnWizardDialog.OK) refreshViewer(null, false);
          }
        };
    actionBarsMenu.add(newRepositoryAction);
  } // contributeActions

  /**
   * @see
   *     org.tigris.subversion.subclipse.ui.repo.RemoteViewPart#addWorkbenchActions(org.eclipse.jface.action.IMenuManager)
   */
  protected void addWorkbenchActions(IMenuManager manager) {
    // New actions go next

    MenuManager sub =
        new MenuManager(
            Policy.bind("RepositoriesView.newSubmenu"),
            IWorkbenchActionConstants.GROUP_ADD); // $NON-NLS-1$
    sub.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    manager.add(sub);

    IStructuredSelection selection = (IStructuredSelection) getViewer().getSelection();

    // File actions go first (view file)
    manager.add(new Separator(IWorkbenchActionConstants.GROUP_FILE));

    if (selection.size() == 1 && selection.getFirstElement() instanceof ISVNRemoteFile) {
      ISVNRemoteFile file = (ISVNRemoteFile) selection.getFirstElement();
      MenuManager submenu =
          new MenuManager(Policy.bind("TreeConflictsView.openWith")); // $NON-NLS-1$
      IWorkbench workbench = SVNUIPlugin.getPlugin().getWorkbench();
      IEditorRegistry registry = workbench.getEditorRegistry();
      IEditorDescriptor[] descriptors = registry.getEditors(file.getName());
      for (IEditorDescriptor descriptor : descriptors) {
        submenu.add(new OpenWithAction(file, descriptor));
      }
      manager.add(submenu);
    }

    // Misc additions
    manager.add(new Separator("historyGroup")); // $NON-NLS-1$
    manager.add(new Separator("checkoutGroup")); // $NON-NLS-1$
    manager.add(new Separator("exportImportGroup")); // $NON-NLS-1$
    manager.add(new Separator("miscGroup")); // $NON-NLS-1$

    manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));

    manager.add(refreshPopupAction);

    removeRootAction.selectionChanged(selection);
    if (removeRootAction.isEnabled()) {
      manager.add(removeRootAction);
    }

    if (selection.size() == 1 && selection.getFirstElement() instanceof ISVNRepositoryLocation) {
      manager.add(new Separator());
      manager.add(propertiesAction);
    }
    sub.add(newAction);
  }

  /*
   * @see WorkbenchPart#createPartControl
   */
  public void createPartControl(Composite parent) {
    treeViewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
    contentProvider = new RemoteContentProvider();
    treeViewer.setContentProvider(contentProvider);
    final RepositoriesViewDecorator decorator = new RepositoriesViewDecorator();
    DecoratingLabelProvider labelProvider =
        new DecoratingLabelProvider(
            new WorkbenchLabelProvider(),
            new ILabelDecorator() {
              public Image decorateImage(Image image, Object obj) {
                if (obj instanceof RemoteFile) {
                  RemoteFile remoteFile = (RemoteFile) obj;
                  if (remoteFile.getLock() != null) {
                    return decorator.getImage(image);
                  }
                }
                return null;
              }

              public String decorateText(String text, Object obj) {
                return null;
              }

              public void addListener(ILabelProviderListener listener) {}

              public void dispose() {}

              public boolean isLabelProperty(Object obj, String prop) {
                return false;
              }

              public void removeListener(ILabelProviderListener listener) {}
            });
    treeViewer.setLabelProvider(labelProvider);

    getSite().setSelectionProvider(treeViewer);
    root = new AllRootsElement();
    treeViewer.setInput(root);
    treeViewer.setSorter(new RepositorySorter());
    drillPart = new DrillDownAdapter(treeViewer);

    contributeActions();

    // F1 Help
    String helpID = getHelpContextId();
    if (helpID != null)
      PlatformUI.getWorkbench().getHelpSystem().setHelp(treeViewer.getControl(), helpID);
    initializeListeners();
    SVNUIPlugin.getPlugin().getRepositoryManager().addRepositoryListener(repositoryListener);
  }

  /** initialize the listeners */
  protected void initializeListeners() {
    getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(this);
    treeViewer.addSelectionChangedListener(removeRootAction);

    // when F5 is pressed, refresh this view
    treeViewer
        .getControl()
        .addKeyListener(
            new KeyAdapter() {
              public void keyPressed(KeyEvent event) {
                if (event.keyCode == SWT.F5) {
                  /*                    IStructuredSelection selection = (IStructuredSelection)getViewer().getSelection();
                                     if (selection.size() == 1)
                                     {
                                         getViewer().refresh(selection.getFirstElement());
                                     }
                  */
                  refreshAction.run();
                }
              }
            });

    treeViewer.addDoubleClickListener(
        new IDoubleClickListener() {
          public void doubleClick(DoubleClickEvent e) {
            handleDoubleClick(e);
          }
        });

    repositoryDragSourceListener = new RepositoryDragSourceListener();
    treeViewer.addDragSupport(
        DND.DROP_LINK | DND.DROP_DEFAULT,
        new Transfer[] {RemoteResourceTransfer.getInstance(), PluginTransfer.getInstance()},
        repositoryDragSourceListener);

    treeViewer.addSelectionChangedListener(
        new ISelectionChangedListener() {
          public void selectionChanged(SelectionChangedEvent event) {
            repositoryDragSourceListener.updateSelection(
                (IStructuredSelection) event.getSelection());
          }
        });
  }

  /**
   * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,
   *     org.eclipse.jface.viewers.ISelection)
   */
  public void selectionChanged(IWorkbenchPart part, ISelection selection) {
    String msg = getStatusLineMessage(selection);
    getViewSite().getActionBars().getStatusLineManager().setMessage(msg);
  }

  /** When selection is changed we update the status line */
  private String getStatusLineMessage(ISelection selection) {
    if (selection == null || selection.isEmpty()) return ""; // $NON-NLS-1$
    if (!(selection instanceof IStructuredSelection)) return ""; // $NON-NLS-1$
    IStructuredSelection s = (IStructuredSelection) selection;

    if (s.size() > 1)
      return Policy.bind(
          "RepositoriesView.NItemsSelected", String.valueOf(s.size())); // $NON-NLS-1$
    Object element = SVNAction.getAdapter(s.getFirstElement(), ISVNResource.class);
    if (element instanceof ISVNRemoteResource) {
      ISVNRemoteResource res = (ISVNRemoteResource) element;
      String name;
      if (res.isContainer()) {
        name = res.getRepositoryRelativePath();
      } else {
        name =
            res.getRepositoryRelativePath()
                + " "
                + ((ISVNRemoteFile) res).getLastChangedRevision(); // $NON-NLS-1$
      }
      return Policy.bind(
          "RepositoriesView.ResourceInRepository",
          name,
          res.getRepository().getLocation()); // $NON-NLS-1$
    }
    return Policy.bind("RepositoriesView.OneItemSelected"); // $NON-NLS-1$
  }

  /** @see org.tigris.subversion.subclipse.ui.repo.RemoteViewPart#getHelpContextId() */
  protected String getHelpContextId() {
    return IHelpContextIds.REPOSITORIES_VIEW;
  }

  /** @see WorkbenchPart#setFocus */
  public void setFocus() {
    treeViewer.getControl().setFocus();
  }

  /**
   * Method getShell.
   *
   * @return Shell
   */
  protected Shell getShell() {
    return treeViewer.getTree().getShell();
  }

  /**
   * Returns the viewer.
   *
   * @return TreeViewer
   */
  protected TreeViewer getViewer() {
    return treeViewer;
  }

  /**
   * this is called whenever a new repository location is added for example or when user wants to
   * refresh
   */
  protected void refreshViewer(Object object, boolean refreshRepositoriesFolders) {
    if (treeViewer == null) return;
    if (refreshRepositoriesFolders) {
      IRunnableWithProgress runnable =
          new IRunnableWithProgress() {
            public void run(IProgressMonitor monitor)
                throws InvocationTargetException, InterruptedException {
              SVNProviderPlugin.getPlugin().getRepositories().refreshRepositoriesFolders(monitor);
            }
          };
      try {
        new ProgressMonitorDialog(getShell()).run(true, false, runnable);
      } catch (Exception e) {
        SVNUIPlugin.openError(getShell(), null, null, e, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
      }
    }
    if (object == null) treeViewer.refresh();
    else {
      if (object instanceof ISVNRemoteFolder) {
        ISVNRemoteFolder parent = ((ISVNRemoteFolder) object).getParent();
        if (parent == null) {
          treeViewer.refresh();
          return;
        }
      }
      treeViewer.refresh(object);
    }
  }

  protected void refreshViewerNode() {
    IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
    Iterator iter = selection.iterator();
    while (iter.hasNext()) {
      Object object = iter.next();
      if (object instanceof ISVNRepositoryLocation) {
        refreshAction.run();
        break;
      }
      if (object instanceof ISVNRemoteFolder) ((ISVNRemoteFolder) object).refresh();
      treeViewer.refresh(object);
    }
  }

  public void collapseAll() {
    if (treeViewer == null) return;
    treeViewer.getControl().setRedraw(false);
    treeViewer.collapseToLevel(treeViewer.getInput(), TreeViewer.ALL_LEVELS);
    treeViewer.getControl().setRedraw(true);
  }

  /** The mouse has been double-clicked in the tree, perform appropriate behaviour. */
  private void handleDoubleClick(DoubleClickEvent e) {
    // Only act on single selection
    ISelection selection = e.getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection structured = (IStructuredSelection) selection;
      if (structured.size() == 1) {
        Object first = structured.getFirstElement();
        if (first instanceof ISVNRemoteFile) {
          // It's a file, open it.
          openAction.selectionChanged(null, selection);
          openAction.run(null);
        } else {
          // Try to expand/contract
          treeViewer.setExpandedState(first, !treeViewer.getExpandedState(first));
        }
      }
    }
  }

  /** @see org.eclipse.ui.IWorkbenchPart#dispose() */
  public void dispose() {
    SVNUIPlugin.getPlugin().getRepositoryManager().removeRepositoryListener(repositoryListener);
    super.dispose();
    treeViewer = null;
  }

  private class OpenWithAction extends Action {
    private ISVNRemoteFile file;
    private IEditorDescriptor editorDescriptor;

    public OpenWithAction(ISVNRemoteFile file, IEditorDescriptor editorDescriptor) {
      super();
      this.file = file;
      this.editorDescriptor = editorDescriptor;
    }

    @Override
    public String getText() {
      return editorDescriptor.getLabel();
    }

    @Override
    public ImageDescriptor getImageDescriptor() {
      return editorDescriptor.getImageDescriptor();
    }

    @Override
    public void run() {
      RemoteFileEditorInput input = new RemoteFileEditorInput(file, null);
      try {
        if (editorDescriptor.isOpenExternal()) {
          input.writeToTempFile();
        }
        IWorkbench workbench = SVNUIPlugin.getPlugin().getWorkbench();
        IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage();
        page.openEditor(input, editorDescriptor.getId());
      } catch (Exception e) {
        MessageDialog.openError(
            getShell(), Policy.bind("OpenRemoteFileAction.0"), e.getMessage()); // $NON-NLS-1$
      }
    }
  }
}
